/*****************************************************************************
*                     Organizacion de Datos - 75.06                          *
*                         Catedra Lic. Servetto                              *
*            Facultad de Ingenieria - Universidad de Buenos Aires            *
******************************************************************************/ 

#include "InterpreteUsuario.h"
#include "AdministradorDeOperaciones.h"
#include "ParseadorComandos.h"

#include <sstream>
#include <iostream>
#include <fstream>
#include <list>

using namespace std;

namespace CapaConsulta
{
	
	void InterpreteUsuario::EjecutarOperaciones(stringstream* entrada)
	{
		string salidaLimpia;
		salidaLimpia = ArreglarEspaciosBlancos(entrada);
		
		list<string>* cadenas;
		cadenas = ExplotarCadenas(salidaLimpia);

		list<Operacion*>* operaciones;
		operaciones = ParsearOperaciones(cadenas);

	};
	
	string InterpreteUsuario::ArreglarEspaciosBlancos(stringstream* entrada)
	{
		string salida;
		
		// Pongo los punteros a cero
		entrada->seekg(0);
		entrada->seekp(0);
		
		// Recorro los caractes eliminado los caracteres molestos (' ', \t, \r y \n) y solo dejando un ' ' en su lugar.
		char caracterALimpiar = entrada->get();
		int numeroCaracter = -1;
		int numeroLinea = 0;
		bool yaPuse = false;
		bool primerMarcaComentario = false;
		bool dentroDeValor = false;
		bool inicial = true;
		bool condicionMayorMenor = false;
		while (caracterALimpiar != EOF)
		{
			numeroCaracter++;
			
			// Detecto el inicio y fin de los datos.
			if (caracterALimpiar == '\'')
			{
				// No puede haber una ' antes que un caracter estandar.
				if (inicial)
				{
					cerr << "No se reconoce el caracter (" << numeroLinea << "," << numeroCaracter << ") como parte de un archivo de operaciones valido." << endl;
					throw new exception();
				}
				
				// Si es la ' del principio y no puse nigun espacio en blanco, se lo agrego.
				if (!dentroDeValor)
				{
					dentroDeValor = true;
					if (!yaPuse)
						salida += ' ';
					
					salida += caracterALimpiar;
				}
				// Si es la ' del final, agrego un espacio en blanco e informo que ya puse ese espacio.
				else
				{
					dentroDeValor = false;
					salida += "\' ";
					yaPuse = true;
				}
				
				caracterALimpiar = entrada->get();
				continue;
			}
			
			// Si estoy limpiando cosas que estan entre comillas simples, dejo todo como esta ya que es un dato.
			if (dentroDeValor)
			{
				salida += caracterALimpiar;
				caracterALimpiar = entrada->get();
				continue;
			}
			
			// Si detecto un -, indica que puede empezar un comentario, los - solos son excluidos.
			if (caracterALimpiar == '-')
			{
				// Si es el primero simplemente me guardo esa info y no hago nada.
				if (!primerMarcaComentario)
				{
					primerMarcaComentario = true;
				}
				// Si es el segundo (osea tengo --) tiro a la basura el resto de la linea por que es un comentario.
				else
				{
					primerMarcaComentario = false;
					string temp;
					getline(*entrada, temp);
					// Informo que avance una linea.
					numeroLinea++;
					numeroCaracter = -1;
				}
				
				caracterALimpiar = entrada->get();
				continue;
			}
			
			// Si tengo algun (o una seguidilla de) caracter(es) de espacio blanco (espacio, tab, retorno de carro o salto de linea) lo reemplazo por un espacio solo.
			if (caracterALimpiar == ' ' || caracterALimpiar == '\t' || caracterALimpiar == '\r' || caracterALimpiar == '\n')
			{
				if (caracterALimpiar == '\n')
				{
					// Informo que avance una linea.
					numeroLinea++;
					numeroCaracter = -1;
				}
				
				if (!yaPuse)
				{
					if (!inicial)
						salida += ' ';
					
					yaPuse = true;
				}
				
				caracterALimpiar = entrada->get();
				continue;
			}
			
			// A los caracteres que separan cosas les agrego espacios blancos a los costados si es que no los tienen.
			if (caracterALimpiar == ',' || caracterALimpiar == '(' || caracterALimpiar == ')')
			{
				if (!yaPuse)
				{
					salida += ' ';
					salida += caracterALimpiar;
					salida += ' ';
				}
				else
				{
					salida += caracterALimpiar;
					salida += ' ';
				}
				
				caracterALimpiar = entrada->get();
				yaPuse = true;
				continue;
			}
			
			if( caracterALimpiar == '>' || caracterALimpiar == '<')
			{
				if (!yaPuse)
					salida += ' ';
				
				condicionMayorMenor = true;
				
				salida += caracterALimpiar;
				caracterALimpiar = entrada->get();
				yaPuse = false;
				continue;
			}
			
			if (caracterALimpiar == '=')
			{
				if (!condicionMayorMenor)
				{
					if (!yaPuse)
						salida += " = ";
					else
						salida += "= ";
				}
				else
					salida += "= ";
				
				condicionMayorMenor = false;
				caracterALimpiar = entrada->get();
				yaPuse = true;
				continue;
			}
			
			if (condicionMayorMenor && !yaPuse)
				salida += ' ';
			
			salida += caracterALimpiar;
			condicionMayorMenor = false;
			caracterALimpiar = entrada->get();
			inicial = false;
			yaPuse = false;
			
		}
		
		return salida;
	};
	
	list<string>* InterpreteUsuario::ExplotarCadenas(string entrada)
	{
		list<string>* palabras = new list<string>();
		string palabra = "";
		bool estoyEnDato = false;

		for(int i = 0; i < entrada.length(); ++i)
		{
			if (entrada[i] == '\'')
			{
				estoyEnDato = !estoyEnDato;
				palabra += entrada[i];
				continue;
			}

			if (estoyEnDato)
			{
				palabra += entrada[i];
				continue;
			}

			if (entrada[i] != ' ')
			{
				palabra += entrada[i];
			}
			else
			{
				palabras->push_back(palabra);
				palabra = "";
			}
		}
		if (palabra != "")
			palabras->push_back(palabra);
		
		return palabras;
	};

	list<Operacion*>* InterpreteUsuario::ParsearOperaciones(list<string>* operacionesSinParsear)
	{
		list<Operacion*>* resultado = new list<Operacion*>();
		list<string>* operacionUnitaria = new list<string>();
		
		int contadorOperaciones = 0;
		bool esPrimera = true;
		operacionesSinParsear->push_back(" ");
		
		for(list<string>::iterator i = operacionesSinParsear->begin(); i != operacionesSinParsear->end(); ++i)
		{
			string palabra = *i;
			
			if (!esPrimera)
			{
				if (palabra == "SELECT" || palabra == "INSERT" || palabra == "UPDATE" || palabra == "DELETE" || palabra == "CREATE" || palabra == "EXECUTE" || palabra == " ")
				{
					contadorOperaciones++;
					cout << "Ejecutando operacion " << contadorOperaciones << " (" << operacionUnitaria->front() << ")." << endl;
					if (operacionUnitaria->front() == "SELECT")
					{
						Operacion* seleccion;
						seleccion = ParseadorComandos::ParsearSeleccion(operacionUnitaria, contadorOperaciones);
						resultado->push_back(seleccion);
						seleccion->Ejecutar();
					}
					else if (operacionUnitaria->front() == "INSERT")
					{
						Operacion* insercion;
						insercion = ParseadorComandos::ParsearInsercion(operacionUnitaria);
						resultado->push_back(insercion);
						insercion->Ejecutar();
					}
					else if (operacionUnitaria->front() == "UPDATE")
					{
						Operacion* actualizacion;
						actualizacion = ParseadorComandos::ParsearActualizacion(operacionUnitaria);
						resultado->push_back(actualizacion);
						actualizacion->Ejecutar();
					}
					else if (operacionUnitaria->front() == "DELETE")
					{
						Operacion* borrado;
						borrado = ParseadorComandos::ParsearBorrado(operacionUnitaria);
						resultado->push_back(borrado);
						borrado->Ejecutar();
					}
					else if (operacionUnitaria->front() == "CREATE")
					{
						Operacion* creacion;
						creacion = ParseadorComandos::ParsearCreacion(operacionUnitaria);
						resultado->push_back(creacion);
						creacion->Ejecutar();
					}
					else if (operacionUnitaria->front() == "EXECUTE")
					{
						Operacion* ejecucion;
						ejecucion = ParseadorComandos::ParsearEjecucion(operacionUnitaria);
						resultado->push_back(ejecucion);
						ejecucion->Ejecutar();
					}
					else
					{
						cerr << "No se reconoce a " << operacionUnitaria->front() << " como una operacion valida." << endl;
						throw new exception();
					}
					
					delete operacionUnitaria;
					operacionUnitaria = new list<string>();
				}
			}
			
			operacionUnitaria->push_back(palabra);
			esPrimera = false;
		}
		
		return resultado;
	};
	
}
